//+--------------------------------------------------------------------
//| Copyright(c) 2012 - an.vuongngocduy					
//| This file is a part of Compact Game Engine (CGE)	
//+--------------------------------------------------------------------
#include "cgeColor.h"

namespace cge
{
	namespace gfx
	{
		/*----------------------------------------------------------------------------------------*/
		Color::Color()
			: Red(0), Green(0), Blue(0), Alpha(0)
		{}

		/*----------------------------------------------------------------------------------------*/
		Color::Color(u8 r, u8 g, u8 b, u8 a)
			: Red(r), Green(g), Blue(b), Alpha(a)
		{}

		/*----------------------------------------------------------------------------------------*/
		Color::Color(u32 rgba)
			: RGBA(rgba)
		{}

		/*----------------------------------------------------------------------------------------*/
		Color::~Color()
		{}

		/*----------------------------------------------------------------------------------------*/
		void Color::Set(u8 r, u8 g, u8 b, u8 a)
		{
			Red = r; Green = g;	Blue = b; Alpha = a;
		}

		/*----------------------------------------------------------------------------------------*/
		void Color::Set(u32 rgba) 
		{ 
			RGBA = rgba; 
		}

		/*----------------------------------------------------------------------------------------*/
		b8 Color::IsTransparent() const
		{
			return (Alpha != 0xFF);
		}

		/*----------------------------------------------------------------------------------------*/
		Color& Color::FromARGB(u32 argb)
		{
			Blue = argb & 0xFF;
			Green = (argb >> 8) & 0xFF;
			Red = (argb >> 16) & 0xFF;
			Alpha = (argb >> 24) & 0xFF;
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		u32 Color::ToARGB() const
		{
			return (Blue | Green << 8 | Red << 16 | Alpha << 24);
		}

		/*----------------------------------------------------------------------------------------*/
		Color& Color::FromFloats(Color::F32RGBA rgba)
		{
			Red = static_cast<u8>(rgba.Red * 255.f);
			Green = static_cast<u8>(rgba.Green * 255.f);
			Blue = static_cast<u8>(rgba.Blue * 255.f);
			Alpha = static_cast<u8>(rgba.Alpha * 255.f);
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		Color::F32RGBA Color::ToFloats() const
		{
			Color::F32RGBA rgba = {0};
			rgba.Red = (f32)(Red) / 255.f;
			rgba.Green = (f32)(Green) / 255.f;
			rgba.Blue = (f32)(Blue) / 255.f;
			rgba.Alpha = (f32)(Alpha) / 255.f;
			return rgba;
		}

		/*----------------------------------------------------------------------------------------*/
		Color& Color::FromHSB(Color::F32HSB hsb)
		{
			Color::F32RGBA clr = {0};

			if (hsb.Brightness == 0.0f)
			{   
				// black
				clr.Red = clr.Green = clr.Blue = 0.0f;
			}
			else if( hsb.Saturation == 0.0f ) 
			{
				// gray
				clr.Red = clr.Green = clr.Blue = hsb.Brightness;
				return *this;
			}
			else
			{
				f32 h1 = hsb.Hue * 6.0f;			// sector 0 to 5
				s32 h2 = (s32)Math::Floor(h1);			
				f32 p = hsb.Brightness * ( 1.0f - hsb.Saturation );
				f32 q = hsb.Brightness * ( 1.0f - hsb.Saturation * (h1 -h2));
				f32 t = hsb.Brightness * ( 1.0f - hsb.Saturation * ( 1 - (h1 - h2)));

				switch( h2 ) 
				{
				case 0:
					clr.Red = hsb.Brightness;
					clr.Green = t;
					clr.Blue = p;
					break;

				case 1:
					clr.Red = q;
					clr.Green = hsb.Brightness;
					clr.Blue = p;
					break;

				case 2:
					clr.Red = p;
					clr.Green = hsb.Brightness;
					clr.Blue = t;
					break;

				case 3:
					clr.Red = p;
					clr.Green = q;
					clr.Blue = hsb.Brightness;
					break;

				case 4:
					clr.Red = t;
					clr.Green = p;
					clr.Blue = hsb.Brightness;
					break;

				default:
					clr.Red = hsb.Brightness;
					clr.Green = p;
					clr.Blue = q;
					break;
				}
			}

			FromFloats(clr);
			return *this;
		}

		/*----------------------------------------------------------------------------------------*/
		Color::F32HSB Color::ToHSB() const
		{
			Color::F32HSB res = {0};
			Color::F32RGBA clr = ToFloats();

			f32 min, max, delta;
			min = Math::Min(3, clr.Red, clr.Green, clr.Blue );
			max = Math::Max(3, clr.Red, clr.Green, clr.Blue );
			res.Brightness = max;					// brightness
			delta = max - min;
			
			if( max != 0 )
			{
				res.Saturation = delta / max;		// saturation
			}
			else 
			{
				// r = g = b = 0					// saturation = 0, brightness is undefined
				res.Saturation = 0;
				res.Hue = -1;
				return res;
			}

			if( clr.Red == max )
			{
				res.Hue = ( clr.Green - clr.Blue ) / delta;		// between yellow & magenta
			}		
			else if( clr.Green == max )
			{
				res.Hue = 2 + ( clr.Blue - clr.Red ) / delta;	// between cyan & yellow
			}
			else
			{
				res.Hue = 4 + ( clr.Red - clr.Green ) / delta;	// between magenta & cyan
			}

			res.Hue /= 6.0f;									// degrees
			
			if(res.Hue < 0.0f)
				res.Hue += 1.0f;
			
			if(res.Hue > 1.0f)
				res.Hue -= 1.0f;

			return res;
		}

		/*----------------------------------------------------------------------------------------*/
		b8 Color::operator == (const Color& col)
		{	
			return (Red == col.Red) &&	(Green == col.Green) && (Blue == col.Blue) && (Alpha == col.Alpha);
		}

		/*----------------------------------------------------------------------------------------*/
		b8 Color::operator != (const Color& col)
		{
			return !(*this == col);
		}
	}
}